<?php
// +----------------------------------------------------------------------
// | ThinkPHP 5 [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2018 .
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
namespace security;

use think\facade\Db;
use think\facade\Config;
use think\facade\Session;
use think\validate\ValidateRule;

/**
 * 权限认证类
 * 功能特性：
 * $auth = \huangsen\auth\Auth::getInstance();  $auth->check('规则名称','用户id')
 */
class Auth
{
    //默认配置
    protected $config = array(
        'auth_on' => true, // 权限开关
        'auth_cache' => true, //是否开启缓存
        'auth_key' => '_auth_', // 数据缓存的key
        'auth' => 't_purview', // 权限表
        'auth_role' => 't_purview_role', // 权限规则表
        'role' => 't_role', // 角色表
        'role_user' => 'user_role', // 用户角色对应表
        'users' => 't_users', // 用户信息表
        'users_auth_fields' => '',//用户需要验证的规则表达式字段 空代表所有用户字段
        //不需要登录的
        'no_need_login_url' => [
            '/admin/login/login'
        ],
        //登录用户不需要验证的
        'allow_visit' => [
            '/admin',
            '/admin/index/main',
            '/admin/login/logout'
        ],
        //默认登陆页面
        'login_path' => '/admin/login/login'
    );

    //用户信息
    protected $userInfo = [];

    //对象实例
    protected static $instance;

    //权限检查模式
    protected $model = 1;

    /**
     * 单列
     * @param array $options 参数
     * @return object|static 对象
     */
    public static function getInstance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($options);
        }
        return self::$instance;
    }

    /**
     * 类架构函数 （私有构造函数，防止外界实例化对象）
     * @param array $options 参数
     * Auth constructor.
     */
    private function __construct($options = [])
    {
        //可设置配置项 auth, 此配置项为数组。 thinkphp5.1 需要配置auth.php
        if ($auth = Config::get('app.admin.auth.')) {
            $this->config = array_merge($this->config, $auth);
        }

        // 将传递过来的参数替换
        if (!empty($options) && is_array($options)) {
            $this->config = array_merge($this->config, $options);
        }
    }

    /**
     * 检查权限
     * @param string $name url路径
     * @param int $uid 用户id
     * @return bool 通过验证返回true;失败返回false
     * @throws \think\exception\DbException
     */
    public function check($name = null, $uid = null)
    {
        is_null($name) && $name = $this->getPath();
        is_null($uid) && $uid = $this->getUserId();
        if (empty($uid) || empty($name)) {
            return false;
        }

        // 是否开启权限开关
        if (empty($this->config['auth_on'])) {
            return true;
        }

        //不需要验证权限路径
        if (in_array($name, $this->config['allow_visit'])) {
            return true;
        }

        //获取用户对应角色
        $purviews = $this->getRoleUser($uid);

        if (empty($purviews)) {
            return false;
        }

        //判断是否有权限使用
        foreach ($purviews as $value) {
            if('/admin' == $value) return true;
            if($name == $value) return true;
        }
        return false;
    }

    /**
     * 获取用户拥有的权限
     * @param $uid int 用户id
     * @return array|bool|mixed 拥有的角色数组
     * @throws \think\exception\DbException
     */
    public function getRoleUser($uid = null)
    {
        is_null($uid) && $uid = $this->getUserId();

        $data = $this->config['auth_cache'] ? Session::get($this->getRoleUserKey()) : [];

        if (empty($data) || !is_array($data)) {

            $purviews = Db::name($this->config['role_user'])
                ->alias('ru')
                ->where("ru.uid = '$uid'")
                ->join($this->config['role'] . ' r', 'ru.role_id = r.role_id')
                ->join($this->config['auth_role'] . ' ar', 'ar.role_id = ru.role_id')
                ->join($this->config['auth'] . ' a', 'a.purview_id = ar.purview_id')
                ->field('a.purview_route')
                ->select()->toArray();

            $purviews = array_column($purviews, 'purview_route');

            foreach ($purviews as $key => $value) {
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                    $purviews[$key] = explode("\n", $value);
                } elseif (strtoupper(substr(PHP_OS, 0, 5)) === 'LINUX'){
                    $purviews[$key] = explode(PHP_EOL, $value);
                }
            }

            $data = array_reduce($purviews, 'array_merge', array());
            if (empty($data)) return false;
            if ($this->config['auth_cache']) Session::set($this->getRoleUserKey(), $data);

        }
        return $data;

    }

    /**
     * 获取用户角色 session key
     * @return string
     */
    private function getRoleUserKey()
    {
        return $this->getKey('role_user_list');
    }

    /**
     * 获取auth 的session key
     * @param $key string 需要获取的key
     * @return string
     */
    private function getKey($key)
    {
        return md5($this->config['auth_key'] . $key);
    }

    /**
     * 根据用户id登录
     * @param null $admin
     * @return bool
     */
    public function login($admin = null)
    {
        if (is_numeric($admin)) {
            $admin = Db::name($this->config['users'])->where($admin)->find()->toArray();
            unset($admin['password']);
        }
        if ($admin) {
            Session::set('uid', $admin['uid']);
            Session::set('role_id', 1);
            Session::set('gold-admin', $admin);
            Session::set($this->getRoleUserKey(), null);
            return true;
        }
        return false;
    }

    /**
     * 退出登陆
     * @return bool
     */
    public function logout()
    {
        session(null);
        $this->userInfo = null;
        return true;
    }

    /**
     * 检查是否登录
     * @return bool
     */
    public function isLogin()
    {
        return !empty($this->getUserInfo());
    }

    /**
     * 当前登录用户
     * @return mixed
     */
    public function getUserInfo()
    {
        $this->userInfo = !empty($this->userInfo) ? $this->userInfo : Session::get('gold-admin');
        return $this->userInfo;
    }

    /**
     * 获取登陆了用户id
     * @return null
     */
    public function getUserId()
    {
        $user = $this->getUserInfo();
        return $user ? $user[$user->getPk()] : null;
    }

    /**
     * 根据用户id获取用户信息
     * @param $uid int 用户id
     * @return array|mixed|null|\PDOStatement|string|\think\Model
     * @throws \think\exception\DbException
     */
    public function getUserInfoById($uid = null)
    {
        is_null($uid) && $uid = $this->getUserId();

        $userinfo = $this->config['auth_cache'] ? Session::get($this->getUserKey($uid)) : [];

        if (empty($userinfo) || !is_array($userinfo)) {
            $user = Db::name($this->config['users']);
            // 获取用户表主键
            $_pk = is_string($user->getPk()) ? $user->getPk() : 'id';

            $userinfo = $user->field($this->config['users_auth_fields'])->where($_pk, $uid)->find();

            if ($this->config['auth_cache'])
                Session::set($this->getUserKey($uid), $userinfo);
        }
        return $userinfo;
    }

    /**
     * 获取用户信息的session key
     * @param $uid int 用户id
     * @return string
     */
    private function getUserKey($uid)
    {
        return $this->getKey('user_info' . $uid);
    }

    /**
     * 校验url，是否需要用户验证
     * @return bool
     */
    public function notNeedLogin()
    {
        $urls = $this->config['no_need_login_url'];
        if (in_array($this->getPath(), $urls)) {
            return true;
        }
        return false;
    }

    /**
     * 获取权限相关配置文件
     * @param null $config
     * @return array|mixed
     */
    public function getConif($config = null)
    {
        if (is_null($config)) {
            $configs = $this->config;
        } else {
            $configs = $this->config[$config];
        }
        return $configs;
    }

    /**
     * 获取path
     * @return string
     */
    public function getPath()
    {
        $isExit = strstr($_SERVER['REQUEST_URI'], '.html');
        if($isExit) {
            $path = substr($_SERVER['REQUEST_URI'],0,strrpos($_SERVER['REQUEST_URI'],".html"));
        }else{
            $path = str_replace('.html','',$_SERVER['REQUEST_URI']);
        }

        return str_replace('.', '/', strtolower($path));
    }
}